#import "AlarmView.h"
#import <appkit/appkit.h>
#import <sys/time.h>
#import <dpsclient/wraps.h>  // For PSxxx functions...

#import "Alarm.h"

static char pathname[1024];

static BOOL getOpenPath(char *buf, char const *theType)
{
    static id openPanel = nil;
    char const *fileTypes[2] = {0,0};
    int opr;

    if (!openPanel)
        openPanel = [OpenPanel new];
    if (theType && *theType)
	fileTypes[0] = theType;
    [NXApp setAutoupdate:NO];
    
    if (*buf)
      opr = [openPanel runModalForDirectory:buf
		 file:NULL types:fileTypes];
    else
      opr = [openPanel runModalForDirectory:"/NextLibrary/Sounds" 
	 file:"Basso.snd" types:fileTypes];
    if (opr) {
	strcpy(buf,[openPanel filename]);
	[NXApp setAutoupdate:YES];
	return YES;
    } else {
	[NXApp setAutoupdate:YES];
	return NO;
    }
}

@implementation AlarmView

- initFrame:(const NXRect *) frameRect
{
  loc.x = bounds.size.width/2;
  loc.y = bounds.size.height/2;
  font = [Font newFont:"Times-Bold" size:64.0 matrix:NX_IDENTITYMATRIX];
  font = [[FontPanel new] panelConvertFont:font];
  [inspectorPanel display];
  time = [[Alarm alloc] init];
  strcpy(timestr,[time timeString]);
  [time setDelegate:self];
  [font_name setStringValue:"Times-Bold 64"];
  return [super initFrame:frameRect];
}

- readDefaults
{
	const char *def_sound, *def_font, *def_mode, *def_time;
	float fsize;
	char fname[FILENAME_MAX];
	int dhour, dmin;
	
	def_sound = NXGetDefaultValue([NXApp appName], "AlarmSound");
	if (!def_sound)
		def_sound = "/NextLibrary/Sounds/Rooster.snd";
	[self openFile:(char *)def_sound];
	def_font = NXGetDefaultValue([NXApp appName], "AlarmFont");
	if (def_font && (sscanf(def_font,"%s %f",fname,&fsize) == 2)) {
		font = [Font newFont:fname size:fsize matrix:NX_IDENTITYMATRIX];
		font = [[FontPanel new] panelConvertFont:font];
		[font_name setStringValue:def_font];
	}
	def_mode = NXGetDefaultValue([NXApp appName], "AlarmMode");
	if (def_mode && strcmp(def_mode,"FADING"))
			[slide_fade selectCellAt:0 :1];
	def_time = NXGetDefaultValue([NXApp appName], "AlarmTime");
	if (def_time && (sscanf(def_time,"%d:%d",&dhour ,&dmin) == 2)) 
	{
		dhour = [self validHour:dhour];
		if (dmin < 0 || dmin > 59) dmin = 0;
		[time setAlarmTime:dhour :dmin];
		[self formatAlarmFields];
	}
	return self;
}

- setFade:sender
{
	slide = [sender selectedRow];
	if (slide)
		NXWriteDefault([NXApp appName], "AlarmMode", "Fade");
	else
		NXWriteDefault([NXApp appName], "AlarmMode", "Slide");
	return self;
}

- oneStep
{
  if (slide)
	  [self moveTime];
  else
	  [self dissolveTime];
  return self;
}

- eraseTime
{
	PSsetgray(0.0);
	PSmoveto(loc.x,loc.y);
	PSshow(timestr);
	return self;
}

- drawTime
{
	if (font_changed) {
		font_changed = NO;
		font = newfont;
		[font set];
	}
	PSsetgray(timeGray);
	PSmoveto(loc.x,loc.y);
	PSshow(timestr);
	return self;
}

- dissolveTime
{
  static float x, y, dis = 1.0, da = -0.005;

  [self eraseTime];
  strcpy(timestr,[time timeString]);
  PSstringwidth(timestr,&x,&y);
  timeGray = dis;
  if (dis >= 0.0 && dis <= 1.0)
  {
	dis += da;
  } else {
	da *= -1;
	if (dis < 0.0) {
		loc.x = ( (random()&32767) / 32767.0) * (bounds.size.width - x);
		loc.y = ( (random()&32767) / 32767.0) * (bounds.size.height - y);
	}
	dis += da;  
  }
  [self drawTime];
  return self;
}

- moveTime
{
  static float dx = 0.2, dy = 0.4;
  static float x, y, tmpy, tmpx;
  
  [self eraseTime];
  strcpy(timestr,[time timeString]);
  PSstringwidth(timestr,&x,&y);
  timeGray = NX_LTGRAY;
  if (fabs(tmpx) >= 1)
  {
	loc.x += dx / (fabs(dx));
	tmpx = 0;
	if (((loc.x + x >= bounds.size.width) && (dx > 0)) ||
	      ((loc.x <= 0.0) && (dx < 0)))
	{
		dx = - (dx / (fabs(dx))) * ((random()&32767) / 32767.0);
	}
  } else   tmpx += dx;
  if (fabs(tmpy) >= 1)
  {
	loc.y += dy / (fabs(dy));
	tmpy = 0;
	if (((loc.y + 64.0 >= bounds.size.height) && (dy > 0)) ||
      		((loc.y <= 0.0) && (dy < 0)))
	{
		dy = - (dy / (fabs(dy))) * ((random()&32767) / 32767.0);
	}
  } else   tmpy += dy;
  [self drawTime];
  return self;
}

- inspector:sender
{
  char buf[MAXPATHLEN];
  
  if (!inspectorPanel)
    {
      sprintf(buf,"%s/Alarm.nib",(char *)[sender moduleDirectory:"Alarm"]);
      [NXApp loadNibFile:buf owner:self withNames:NO];
    }
  [self readDefaults];
  [self makeDummyFirstResponder];
  return inspectorPanel;
}

- (BOOL)useBufferedWindow
{
  return YES;
}


- (const char *)windowTitle
{
  return "Alarm!";
}


- openFile:(char *)fileName
{
    id newSound;
    newSound = [[Sound alloc] initFromSoundfile:fileName];
    [self setAlarmSound:newSound];
	[sound_name setStringValue:(strrchr(fileName,'/') + 1)];
    return self;
}

- open:sender
{
    if (getOpenPath(pathname,"snd")) {
		[self openFile:pathname];
		NXWriteDefault([NXApp appName], "AlarmSound", pathname);
	}
    return self;
}

- setAlarmSound:newsound
{
	[alarmSound free];
	alarmSound = newsound;
	[alarmSound setDelegate:self];
	return self;
}

- playAlarmSound:sender
{
	NXEvent dummy;
	if (([alarmSound status] == NX_SoundPlaying) && (sender != self))
		[alarmSound stop];
	else
		[alarmSound play];
	// we do this so that sounds actually play when we're in screensaver
	// mode.  It's necessary because BackSpace doesn't read in events when
	// in screensaver mode, and the soundkit depends on receiving messages from
	// the sound-playing thread.
	[NXApp peekNextEvent:NX_ALLEVENTS into:&dummy];
	return self;
}

- alarm:sender
{
	[self playAlarmSound:self];
	return self;
}

- formatAlarmFields
{
	char tmp[10];
	int h, m;
	
	h = [time alarmHour];
	m = [time alarmMinute];
	[hourSlider setIntValue:h];
	[minSlider setIntValue:m];
	sprintf(tmp,"%d:%d",h,m);
	NXWriteDefault([NXApp appName], "AlarmTime", tmp);
	if (h == 0)
		h = 12;
	if (h > 12) h -= 12;
	sprintf (tmp, "%02d",h);
	[aHour setStringValue:tmp];
	sprintf (tmp, "%02d",m);
	[aMin setStringValue:tmp];
	return self;
}

- (int) validHour:(int)val
{
	if (val < 0 || val >= 24)
		return [time alarmHour];
	if (val < 12)
	{
		[am_pm selectCellAt:0 :0];
		return val;
	}
	[am_pm selectCellAt:1 :0];
	return val;
}

- setTime:sender
{
	int h, m;
	h = [aHour intValue];
	h = [self validHour:h];
	m = [aMin intValue];
	if (m < 0 || m > 59)
		m = [time alarmMinute];
	[time setAlarmTime:h :m];
	[self formatAlarmFields];
	[self resetAlarm];
	return self;
}

- setTimeWithSlider:sender
{
	int h, m;
	h = [hourSlider intValue];
	m = [minSlider intValue];
	h = [self validHour:h];
	[time setAlarmTime:h :m];
	[self formatAlarmFields];
	[self resetAlarm];
	return self;
}

- resetAlarm
{
	// if the alarm might be ringing, reset it so that it stops
	if ([time alarmState]) {
		[time setAlarmState:NO];
		[time setAlarmState:YES];
		if ([alarmSound status] == NX_SoundPlaying)
			[alarmSound stop];
	}
	return self;
}

- setAlarm:sender
{
	if ((BOOL)[sender intValue] == NO)
		[alarmSound stop];
	[time setAlarmState:(BOOL)[sender intValue]];
	return self;
}


// the following is a set of hacks so we can get the font panel to set
// info on something that really isn't the first responder... 
// it's very gross, you probably want to ignore it.

- (BOOL) acceptsFirstResponder
{
	return YES;
}

- makeDummyFirstResponder
{
	NXRect frm;
	NXSetRect(&frm,0,0,1,1);
	dummyView = [[DummyResponderView alloc] initFrame:&frm];
	[inspectorPanel addSubview:dummyView];
	[dummyView setDelegate:self];
	return self;
}

- changeFont:sender
{
	char fontname[1024];
	id mgr;
	mgr = [FontManager new];
	newfont = [mgr convertFont:font];
	font_changed = YES;
	sprintf (fontname,"%s %f",[newfont name],[newfont pointSize]);
	NXWriteDefault([NXApp appName], "AlarmFont", fontname);
	[font_name setStringValue:fontname];
	return self;
}

- setAlarmFont:sender
{
	id mgr;
	
	mgr = [FontManager new];
	// for some weird reason, the panel must be ordered front
	// before the setSelFont will work.
	[mgr orderFrontFontPanel:sender];
	[mgr setSelFont:font isMultiple:NO];
	[[inspectorPanel window] makeFirstResponder:dummyView];
	[[self window] makeFirstResponder:self];
	return self;
}

// BackSpace delegate methods

- enteredScreenSaverMode
{
	[time resetRinger];
	return self;
}

- willExitScreenSaverMode
{
	[alarmSound stop];
	return self;
}

- newWindow
{
	[self becomeFirstResponder];
	return self;
}

- didLockFocus
{
	[font set];
	return self;
}

@end;


@implementation DummyResponderView

- setDelegate:sender
{
	delegate = sender;
	return self;
}

- (BOOL)acceptsFirstResponder
{
	return YES;
}

- changeFont:newfont
{
	[delegate changeFont:newfont];
	return self;
}

@end;